<template>
  <div class="shell-container">
    <div id="shell"/>
  </div>
</template>

<script>

import 'xterm/css/xterm.css'
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'

export default {
  name: 'WebShell',
  props: {
    socketURI: {
      type: String,
      default: import.meta.env.VITE_APP_WEBSOCKET_API,
      //default: 'ws://127.0.0.1:9020/web-terminal'
    },
  },
  watch: {
    socketURI: {
      deep: true, //对象内部属性的监听，关键。
      immediate: true,
      handler() {
        //this.initSocket();
      },
    },
  },
  data() {
    return {
      command: "",
      term: undefined,
      rows: 24,
      cols: 80,
      path: "",
      isShellConn: false // shell是否连接成功
    }
  },
  mounted() {
    const { onTerminalResize } = this;
    this.initSocket();
    // 通过防抖函数
    const resizedFunc = this.debounce(function() {
      onTerminalResize();
    }, 250); // 250毫秒内只执行一次
    window.addEventListener('resize', resizedFunc);
  },
  beforeUnmount() {
    this.socket.close();
    this.term&&this.term.dispose();
    window.removeEventListener('resize');
  },
  methods: {
    initTerm() {
      let term = new Terminal({
        rendererType: "canvas", //渲染类型
        rows: this.rows, //行数
        cols: this.cols, // 不指定行数，自动回车后光标从下一行开始
        convertEol: true, //启用时，光标将设置为下一行的开头
        disableStdin: false, //是否应禁用输入
        windowsMode: true, // 根据窗口换行
        cursorBlink: true, //光标闪烁
        theme: {
          foreground: "#ECECEC", //字体
          background: "#000000", //背景色
          cursor: "help", //设置光标
          lineHeight: 20,
        },
      });
      this.term = term;
      const fitAddon = new FitAddon();
      this.term.loadAddon(fitAddon);
      this.fitAddon = fitAddon;
      let element = document.getElementById("shell");
      term.open(element);
      // 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸)，初始化的时候宽高都是对的
      fitAddon.fit();
      term.focus();
      //监视命令行输入
      this.term.onData((data) => {
        let _this = this
        let dataWrapper = data;
        term.onKey(e => {
          // back 删除的情况
          if (e.domEvent.keyCode === 8) {
            if (term._core.buffer.x > 2) {
              this.command.substring(0, this.command.length -1)
              //dataWrapper == _this.command
              //_this.term.write(this.command);
              // this.term.
            }
          }
        })

        _this.term.write(dataWrapper);
        if (dataWrapper === "\r") {
          dataWrapper = "\n";
        } else if (dataWrapper === "\u0003") {
          // 输入ctrl+c
          dataWrapper += "\n";
        }else if (dataWrapper !== "\n") {
          this.command += dataWrapper
          return;
        }
        this.command += "\n";
        // 将输入的命令通知给后台，后台返回数据。
        this.socket.send(JSON.stringify({ type: "command", data: this.command }));
        this.command = "";
      });
    },
    onTerminalResize() {
      this.fitAddon.fit();
      this.socket.send(
          JSON.stringify({
            type: "resize",
            data: {
              rows: this.term.rows,
              cols: this.term.cols,
            }
          })
      );
    },
    initSocket() {
      if (this.socketURI == "") {
        return;
      }
      // 添加path、cols、rows
      //const uri = `${this.socketURI}&path=${this.path}&cols=${this.cols}&rows=${this.rows}`;
      //ws://api.spinx.cn:9020/web-terminal&searchType=1&chapterld=37
      const uri = `${this.socketURI}`;
      console.log(uri);
      this.socket = new WebSocket(uri);
      this.socketOnClose();
      this.socketOnOpen();
      this.socketOnmessage();
      this.socketOnError();
    },
    socketOnOpen() {
      this.socket.onopen = () => {
        console.log("websocket链接成功");
        this.initTerm();
      };
    },
    socketOnmessage() {
      this.socket.onmessage = (evt) => {
        try {
          if (typeof evt.data === "string") {
            //const msg = JSON.parse(evt.data);
            const msg = evt;
            this.term.write(msg.data);
            // switch(msg.type) {
            //   case "command":
            //     // 将返回的数据写入xterm，回显在webshell上
            //     this.term.write(msg.data);
            //     // 当shell首次连接成功时才发送resize事件
            //     if (!this.isShellConn) {
            //       // when server ready for connection,send resize to server
            //       this.onTerminalResize();
            //       this.isShellConn = true;
            //     }
            //     break;
            //   case "exit":
            //     this.term.write("Process exited with code 0");
            //     break;
            // }
          }
        } catch (e) {
          console.error(e);
          console.log("parse json error.", evt.data);
        }
      };
    },
    socketOnClose() {
      this.socket.onclose = () => {
        this.socket.close();
        console.log("关闭 socket");
        window.removeEventListener("resize", this.onTerminalResize);
      };
    },
    socketOnError() {
      this.socket.onerror = () => {
        console.log("socket 链接失败");
      };
    },
    debounce(func, wait) {
      let timeout;
      return function() {
        const context = this;
        const args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(function() {
          func.apply(context, args);
        }, wait);
      };
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#shell {
  width: 100%;
  height: 100%;
}
.shell-container {
  height: 100%;
}
</style>
